//
// originalview.cpp
//
// The code maintaining the original view of the input images
//

#include "impressionist.h"
#include "impressionistDoc.h"
#include "ImpressionistUI.h"
#include "originalview.h"

#ifndef WIN32
#define min(a, b)	( ( (a)<(b) ) ? (a) : (b) )
#endif
 
#ifndef _MOUSE_MOVEMENTS_
#define _MOUST_MOVEMENTS_
#define LEFT_MOUSE_DOWN		1
#define LEFT_MOUSE_DRAG		2
#define LEFT_MOUSE_UP		3
#define RIGHT_MOUSE_DOWN	4
#define RIGHT_MOUSE_DRAG	5
#define RIGHT_MOUSE_UP		6
#define FIRST_SEED			7
#endif

static Point point;		//origin is left bottom
static int		eventToDo;
static int		isAnEvent=0;
static int		isSeeding=0;
static list<Point> allPoints;
static int		counter[64];

OriginalView::OriginalView(int			x, 
						   int			y, 
						   int			w, 
						   int			h, 
						   const char*	l)
							: Fl_Gl_Window(x,y,w,h,l)
{
	m_nWindowWidth	= w;
	m_nWindowHeight	= h;
	Fl::focus(this);
}

void OriginalView::draw()
{
	Fl::focus(this);	//need this to get keyboard event
	if(!valid())
	{
		glClearColor(0.7f, 0.7f, 0.7f, 1.0);

		// We're only using 2-D, so turn off depth 
		glDisable( GL_DEPTH_TEST );

		// Tell openGL to read from the front buffer when capturing
		// out paint strokes 
		glReadBuffer( GL_FRONT );
		ortho();

	}

	glClear( GL_COLOR_BUFFER_BIT );

	// If debug mode is on, draw debug graph correspondingly
	if (m_pDoc->m_currentPaintMode)
	{
		double drawWidth = m_pDoc->m_nWidth * 3;
		double drawHeight = m_pDoc->m_nHeight * 3;
		resizeWindow (drawWidth,drawHeight);
		GLvoid* bitstart;

		Point scrollpos;//= GetScrollPosition();
		scrollpos.x=scrollpos.y=0;

		int	startrow	= m_pDoc->m_nHeight - (scrollpos.y + drawHeight);
		if ( startrow < 0 ) 
			startrow = 0;

		if (m_pDoc->m_currentPaintMode==kPaintPixelGraph)
		{
			bitstart = m_pDoc->m_debugPixelNode + 9 * ((m_pDoc->m_nWidth * startrow) + scrollpos.x);
		}
		else
		{
			bitstart = m_pDoc->m_debugCostLink + 9 * ((m_pDoc->m_nWidth * startrow) + scrollpos.x);
		}

		// just copy image to GLwindow conceptually
		glRasterPos2i( 0, 0);//m_nWindowHeight - drawHeight );
		glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
		glPixelStorei( GL_UNPACK_ROW_LENGTH, m_pDoc->m_nWidth * 3);
		glDrawBuffer( GL_BACK );
		glDrawPixels( drawWidth ,drawHeight, GL_RGB, GL_UNSIGNED_BYTE, bitstart ); 

		// draw minimum path
		if (m_pDoc->m_currentPaintMode==kPaintMinPath && isSeeding){
			Point temp;
			temp.x=point.x /3;
			temp.y = point.y/3;

			list<Point> min_path = m_pDoc->cal_min_path(temp);
			for (list<Point>::iterator it = min_path.begin(); it != min_path.end(); it++){
				drawMinPathPoint(*it, drawHeight);
			}
		}

		// draw the cost tree
		if (m_pDoc->m_currentPaintMode==kPaintCostTree){
			int i =0;
			for (list<Point>::iterator it = m_pDoc->m_traverseList.begin(); i<m_pDoc->numExpandedNodes; it++){
				i++;
				int color = 120 - 100 * i / m_pDoc->numExpandedNodes;
				if (color >=100) color =100;
				else if (color <=20)  color = 20;
				this->drawTreePoint(*it, drawHeight, color);
			}
		}
	}

	// If debug mode is off, draw original picture.
	else if ( m_pDoc->m_ucBitmap ) 
	{
		//printf("Original size %d %d\n",m_pDoc->m_nWidth,m_pDoc->m_nHeight);
		double tmp_drawWidth,tmp_drawHeight;
		tmp_drawWidth = m_pDoc->m_nWidth * m_pDoc->m_scale;
		tmp_drawHeight = m_pDoc->m_nHeight * m_pDoc->m_scale;
		//printf("tmp: %lf %lf\n",tmp_drawWidth,tmp_drawHeight);
		//resize window if necessary, added by zhaohua
		double cur_windowHeight = min(VIEW_HEIGHT,(int)tmp_drawHeight);
		double cur_windowWidth = min(VIEW_WIDTH, (int)tmp_drawWidth);
		resizeWindow(min( VIEW_WIDTH,(int)tmp_drawWidth),min( VIEW_HEIGHT, (int)tmp_drawHeight));
		// note that both OpenGL pixel storage and the Windows BMP format
		// store pixels left-to-right, BOTTOM-to-TOP!!  thus all the fiddling
		// around with startrow.
		m_nWindowWidth=w();
		m_nWindowHeight=h();

		int drawWidth, drawHeight;
		GLvoid* bitstart;

		// we are not using a scrollable window, so ignore it
		Point scrollpos;	// = GetScrollPosition();
		scrollpos.x=scrollpos.y=0;

		drawWidth	= min( m_nWindowWidth, /*m_pDoc->m_nWidth*/(int)tmp_drawWidth );
		drawHeight	= min( m_nWindowHeight, /*m_pDoc->m_nHeight*/(int)tmp_drawHeight ); // modified by zhaohua for zooming
		//printf("drawWidth, drawHeight: %d %d\n",drawWidth,drawHeight);
		//printf("nWindow: %d %d\n",m_nWindowWidth,m_nWindowHeight);

		int	startrow	= cur_windowHeight/*m_pDoc->m_nHeight*/ - (scrollpos.y + drawHeight);
		if ( startrow < 0 ) 
			startrow = 0;
		//printf("startrow %d\n",startrow);

		bitstart = m_pDoc->m_ucBitmap + 3 * ((m_pDoc->m_nWidth * startrow) + scrollpos.x);
		//printf("scrollpos.x and y %d %d\n",scrollpos.x,scrollpos.y);

		// just copy image to GLwindow conceptually
		glRasterPos2i( 0, 0);//m_nWindowHeight - drawHeight );
		glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
		glPixelStorei( GL_UNPACK_ROW_LENGTH, m_pDoc->m_nWidth );
		glDrawBuffer( GL_BACK );
		glPixelZoom( (GLfloat)m_pDoc->m_scale,(GLfloat)m_pDoc->m_scale);
		glDrawPixels( m_pDoc->m_nWidth, m_pDoc->m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, bitstart );  // (drawWidth,drawHeight,...)
		//printf("bitstart %d\n",bitstart);
		// draw path
		if(isSeeding) {
			// draw "Live wire" 
			list<Point> min_path = m_pDoc->cal_min_path(point);
			for (list<Point>::iterator it = min_path.begin(); it != min_path.end(); it++){
				drawPoint(*it);
			}
		}
		// draw previous contour
		for (list<Point>::iterator it = allPoints.begin(); it!= allPoints.end(); it++){
			drawPoint (*it);
		}

	}

			
	glFlush();
}

void OriginalView::refresh()
{
	redraw();
}

void OriginalView::resizeWindow(int	width, 
								int	height)
{
	resize(x(), y(), width, height);
	glLoadIdentity();
	glViewport (0, 0, width,height);
	ortho();
	redraw();
}

Point OriginalView::GetScrollPosition()
{
	Point pt;
	pt.x = this->x();
	pt.y = this->y();
	printf("OriginalView position: %lf %lf",pt.x,pt.y);
	return pt;
}

int OriginalView::handle(int event)
{
	if (!m_pDoc->m_scale) return 0;
	Point coord;
	coord.x= Fl::event_x()/m_pDoc->m_scale;
	coord.y= m_pDoc->m_nHeight - Fl::event_y()/m_pDoc->m_scale;
	if (!m_pDoc->in_valid_range(coord.y,coord.x))
		return 0;
//	printf ("cursor at: %d %d \n", coord.x, coord.y);
//	printf ("window w and h: %d %d \n",  m_pDoc->m_nWidth,  m_pDoc->m_nHeight);
	
	int key = 0;

	// in debug mode, only handle cursor move
	if (m_pDoc->m_currentPaintMode) {
		switch(event){
		case FL_ENTER:
			redraw();
			break;
		case FL_MOVE:
			coord.y = m_pDoc->m_nHeight * 3 - Fl::event_y();
			point = coord;
			redraw();
			break;
		default:
			return 0;
			break;
		}
		return 1;
	}

	switch (event)
	{
	case FL_ENTER:
		redraw();
		break;
	case FL_PUSH:
		if (Fl::event_ctrl())
		{
			m_pDoc->clear_seedPoint();
			allPoints.clear();
			isSeeding = 1;
		}
		if (isSeeding)
		{
			m_pDoc->add_seedPoint(coord);	
			printf("seed list size: %d \n",m_pDoc->m_seedPoints.size());
			//save contour for previous seeds
			int size = m_pDoc->m_seedPoints.size();
			if (size>1){
				list<Point> temp = m_pDoc->cal_min_path(coord);
				counter[size-2] = 0;
				for (list<Point>::iterator it = temp.begin(); it != temp.end(); it++){
					allPoints.push_back(*it);
					counter[size-2]++;
				}
				printf ("all points list size: %d \n", allPoints.size());
			}
			// calculate path tree for the seed; time consuming step here
			m_pDoc->cal_path_tree(m_pDoc->m_seedPoints.back());		
		}
		point = coord; 
		redraw();
		break;
	case FL_MOVE:
		//printf ("mouse position: %d %d \n", coord.x,coord.y);
		point = coord;
		redraw();
		break;
	case FL_KEYBOARD:
		key = Fl::event_key();
		if ( key == 65288)		//backspace
		{
			if(isSeeding)
			{
				m_pDoc->remove_seedPoint();
				int size = m_pDoc->m_seedPoints.size();
				
				if (size){
					//re-calculate path tree for previous point
					m_pDoc->cal_path_tree(m_pDoc->m_seedPoints.back());	
					// remove contour for last point
					for (int i=0;i<counter[size - 1];i++) allPoints.pop_back();
					printf ("all points list size: %d \n", allPoints.size());
					printf("seed list size: %d \n",m_pDoc->m_seedPoints.size());
				}				
				//exit seeding if seed list is empty
				else{
					isSeeding = 0;
					allPoints.clear();
				}			
			}
			else
			{
				// remove contour here
				allPoints.clear();
				m_pDoc->closedContour.clear();
			}
		}
		else if (key==65293)	//enter
		{
			isSeeding = 0;
			if (Fl::event_ctrl())
			{
				// close contour based on minimum path
				list<Point> temp = m_pDoc->cal_min_path( m_pDoc->m_seedPoints.front());
				for (list<Point>::iterator it = temp.begin(); it != temp.end(); it++){
					allPoints.push_back(*it);
				}
			}
			// save finished contour to Doc
			for (list<Point>::iterator it = allPoints.begin();it !=allPoints.end();it++){
				m_pDoc->closedContour.push_back(*it);
			}
		}
		else if (key==61)	// '='
		{
			// perform zoom in 
			if (Fl::event_ctrl()){
				m_pDoc->m_scale += 0.25;
				m_pDoc->m_scale = min(2.0,m_pDoc->m_scale);
				redraw();
			}
		}

		else if (key==45)// '-'
		{
			// perform zoom out
			if(Fl::event_ctrl()){
				m_pDoc->m_scale -=0.25;
				m_pDoc->m_scale = max(0.5,m_pDoc->m_scale);
				redraw();
			}
		}

		break;
	default:
		return 0;
		break;
	}	
	return 1;
}

void OriginalView::drawPoint(Point pt)
{
	glPointSize (1.0);
	glBegin(GL_POINTS);
		if(isSeeding) glColor3b(100,0,0); //Set it as red
		else glColor3b (0,100,0);
		glVertex2d(pt.x * m_pDoc->m_scale,pt.y* m_pDoc->m_scale);
	glEnd();
}

void OriginalView::drawMinPathPoint(Point pt, int h){
	glPointSize(3.0);
	glBegin(GL_POINTS);
		glColor3b(100,100,0);
		glVertex2d(pt.x * 3, pt.y*3);
	glEnd();
}

void OriginalView::drawTreePoint(Point pt, int h, int color){
	glPointSize(1.5);
	glBegin(GL_POINTS);
		glColor3b (color, color, 0);
		glVertex2d (pt.x * 3, pt.y*3);
	glEnd();
}

void OriginalView::cleanUpGlobals(){
	allPoints.clear();
	isSeeding = 0;
	point.x= 0;
	point.y =0;
}



